package data;

import java.security.MessageDigest;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import sun.misc.BASE64Encoder;

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import database.ConnectionHandler;

public class VotesCounter
{
    private RSAPrivateKey _pk2; // to jest prywatny od tego publicznego, ktorym
                                // koduje klient swoja
                                // wysylana karte.
    private RSAPublicKey _pk; // public key - e w RSA. To jest ten sam, ktorym
                              // klient zaciemnia,
                              // i ten, ktory zdejmuje podpis serwera na karcie.
    private Map<String, Integer> _countedVotes;
    private Set<String> _countedHashes;

    public VotesCounter( RSAPublicKey pk, RSAPrivateKey pk2 )
    {
        _pk = pk;
        _pk2 = pk2;
    }

    public Map<String, Integer> count()
    {
        ConnectionHandler connHand = new ConnectionHandler();
        Connection c = connHand.getConnection();
        List<String> codedVotes = Lists.newLinkedList();
        try
        {
            Statement st2 = c.createStatement();
            ResultSet rs2 = st2.executeQuery( "select * from \"codedVotes\" order by time asc" );
            while( rs2.next() )
            {
                codedVotes.add( rs2.getString( "data" ) );
            }
        }
        catch( Exception e )
        {
            throw new RuntimeException( e );
        }
        Map<VotingCard, String> cards = Maps.newLinkedHashMap();
        Blind b = new Blind();
        MessageDigest md5 = null;
        try
        {
            md5 = MessageDigest.getInstance( "MD5" );
        }
        catch( Exception e )
        {
        }
        BASE64Encoder encoder = new BASE64Encoder();
        for( String code: codedVotes )
        {
            String tempx = b.SecUnhash( code, _pk2 );
            VotingCard card = b.unsign( tempx, _pk );
            // System.out.println( card );
            cards.put( card, encoder.encode( md5.digest( code.getBytes() ) ) );
        }
        for( Entry<VotingCard, String> card: cards.entrySet() )
        {
            Candidate candidate;
            try
            {
                candidate = card.getKey().getVoted().get( 0 );
                // System.out.println( candidate );
            }
            catch( Exception e )
            {
                continue;
            }
            String[] splitted = candidate.getName().split( " " );
            String id_candidate = splitted[ 0 ];
            String id_card = card.getKey().getId().toString();
            try
            {
                Statement st = c.createStatement();
                String sql = "select * from register( '" + id_card + "', '" + id_candidate + "', '" + card.getValue() + "')";
                st.executeQuery( sql );
            }
            catch( Exception e )
            {
            }
        }
        _countedVotes = Maps.newLinkedHashMap();
        try
        {
            Statement st = c.createStatement();
            ResultSet rs = st.executeQuery( "SELECT * FROM candidate order by numberofvotes desc" );
            while( rs.next() )
            {
                String first = rs.getString( "firstname" );
                String second = rs.getString( "lastname" );
                Integer number = rs.getInt( "numberofvotes" );
                _countedVotes.put( first + " " + second, number );
            }
        }
        catch( Exception e )
        {
        }
        _countedHashes = Sets.newHashSet();
        try
        {
            Statement st = c.createStatement();
            ResultSet rs = st.executeQuery( "SELECT originaldigest from votes" );
            while( rs.next() )
            {
                _countedHashes.add( rs.getString( 1 ) );
            }
        }
        catch( Exception e )
        {
        }
        return _countedVotes;
    }

    public String votesAsString()
    {
        Preconditions.checkNotNull( _countedVotes );
        StringBuilder builder = new StringBuilder( "Voting results:\r\n" );
        for( Entry<String, Integer> e: _countedVotes.entrySet() )
        {
            builder.append( e.getKey() + " number of votes: " + e.getValue() + "\r\n" );
        }
        return builder.toString();
    }

    public String showAllValidVotesHashes()
    {
        Preconditions.checkNotNull( _countedHashes );
        StringBuilder builder = new StringBuilder( "Valid votes:\r\n" );
        for( String h: _countedHashes )
            builder.append( h + "\r\n" );
        return builder.toString();
    }

    public boolean wasVoteCounted( String hash )
    {
        return _countedHashes.contains( hash );
    }
}
